package de.khaled.diplom;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class FischerMarkt {

	private TestDatenGenerator tdg = new TestDatenGenerator(100, 60,
			TestDatenGenerator.Bereiche.KauferBereich,
			TestDatenGenerator.Bereiche.NutzenBereich);

	private IdentifikationsGraph identi_graph = new IdentifikationsGraph();
	private List<Kante> geschichteNetzwerk = new ArrayList<>();
	private double m1;
	private double m2;
	private double preisErhoehung;
	private int letzteSchicht = 1;
	private double gesamtGutPreis = 0;

	public void setGesamtGutPreis(double preis) {
		this.gesamtGutPreis = preis;
	}

	public double getGesamtGutPreis() {
		return this.gesamtGutPreis;
	}

	public int getLetzteSchicht() {
		return this.letzteSchicht;
	}

	public void setLetzteSchicht(int schicht) {
		this.letzteSchicht = schicht;
	}

	public void setM1(double m1) {
		this.m1 = m1;
	}

	public void setM2(double m2) {
		this.m2 = m2;
	}

	public void berechneGesamtGueterPreis() {
		double temp_gesamt_gueter_preis = 0;
		for (Gut gut : this.tdg.gut_liste) {
			if (gut.isSelektiert()) {
				temp_gesamt_gueter_preis = temp_gesamt_gueter_preis
						+ round(gut.getPreis());
			}
		}
		this.setGesamtGutPreis(temp_gesamt_gueter_preis);
	}

	public void saturiere(Kaeufer kaeufer) {
		this.setM1(0);
		this.setM2(0);
		this.setPreisErhoehung(0);
		this.bilde_geschichtete_netzwerk(kaeufer);
		this.berechneM1();
		this.berechneM2();
		this.berechnePreiserhoehung(kaeufer);
		this.verteilePreiserhoehung();
		while (!kaeufer.isSaturiert()) {
			this.berechneRelativeNutzenFuerKaeufer();
			this.bauIdentifikationsgraph();
			this.bilde_geschichtete_netzwerk(kaeufer);
			this.berechneGesamtGueterPreis();
			this.berechneM1();
			this.berechneM2();
			this.berechnePreiserhoehung(kaeufer);
			this.verteilePreiserhoehung();
			if (kaeufer.getRestKapital() == 0) {
				kaeufer.setSaturiert(true);
				System.out.println(kaeufer.getName() + " ist saturiert");
				kaeufer.setSelektiert(false);
				geschichteNetzwerk.clear();
			}
		}
	}

	public void verteilePreiserhoehung() {
		for (Gut gut : this.tdg.gut_liste) {
			if (gut.isSelektiert()) {
				gut.setPreis((round(gut.getPreis() / this.getGesamtGutPreis()
						* this.getPreisErhoehung()))
						+ round(gut.getPreis()));
			}
		}
	}

	public void berechnePreiserhoehung(Kaeufer kaeufer_zu_saturieren) {
		double tmp_array[] = { m1, m2, kaeufer_zu_saturieren.getRestKapital() };
		Arrays.sort(tmp_array);
		if (tmp_array[0] == 0) {
			this.preisErhoehung = tmp_array[1];

		} else {
			this.preisErhoehung = tmp_array[0];
		}

		kaeufer_zu_saturieren.setRestKapital(round(kaeufer_zu_saturieren
				.getRestKapital()) - round(this.preisErhoehung));
		kaeufer_zu_saturieren.setmOut(round(kaeufer_zu_saturieren.getmOut())
				+ round(this.preisErhoehung));
	}

	public void berechneM2() {
		this.m2 = 0.0;
		berechne_preissumme_der_selektierte_gueter();
		berechneKaeuferM2UM2();
	}

	public void berechneKaeuferM2UM2() {
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			if (kaeufer.isSelektiert() && kaeufer.getBeta() > 0) {
				kaeufer.setM2(round(((kaeufer.getAlpha() / kaeufer.getBeta()) - 1)
						* this.getGesamtGutPreis()));
				if ((this.m2 == 0.0) || (this.m2 >= kaeufer.getM2())) {
					this.m2 = kaeufer.getM2();
				}
			}
		}
	}

	private double berechne_preissumme_der_selektierte_gueter() {
		double temp_summe = 0.0;
		for (Gut gut : this.tdg.gut_liste) {
			if (gut.isSelektiert() == true) {
				temp_summe += gut.getPreis();
			}
		}
		this.setGesamtGutPreis(round(temp_summe));
		return round(this.gesamtGutPreis);
	}

	public void berechneM1() {
		this.m1 = 0.0;
		double temp_letzte_schicht = this.letzteSchicht;
		while (temp_letzte_schicht >= 2) {
			berechneGesamteAnfragen(temp_letzte_schicht);
			bezahleGesamteAnfragen(temp_letzte_schicht - 1);
			temp_letzte_schicht = temp_letzte_schicht - 2;
		}
	}

	private void bezahleGesamteAnfragen(double kaeufer_schicht) {
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			if (kaeufer.isSelektiert()
					&& kaeufer.getSchicht() == kaeufer_schicht) {
				if (!kaeufer.isSaturiert()) {
					kaeufer.setM1(round(kaeufer.getRestKapital()
							/ kaeufer.getSumAnfragen()));
					if (this.m1 > kaeufer.getM1() || this.m1 == 0) {
						this.m1 = round(kaeufer.getM1());
					}
				} else {
					kaeufer.setM1(round(kaeufer.getmOut()
							/ kaeufer.getSumAnfragen()));
					if (this.m1 > kaeufer.getM1() || this.m1 == 0) {
						this.m1 = round(kaeufer.getM1());
					}
				}
			}
		}
	}

	public void printTicket() {
		double m_out_summe = 0;
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			System.out
					.println("------------------------------------------------------------------------------");
			System.out.println(kaeufer.getName() + " ist saturiert: "
					+ kaeufer.isSaturiert());
			System.out.println("M_out von " + kaeufer.getName() + " ist: "
					+ kaeufer.getmOut());
			System.out.println("Restkapital " + kaeufer.getName() + " ist: "
					+ kaeufer.getRestKapital());
			m_out_summe += kaeufer.getmOut();

		}

		for (Gut gut : this.tdg.gut_liste) {
			System.out
					.println("------------------------------------------------------------------------------");
			System.out.println(gut.getName() + " kostet: " + gut.getPreis());
		}

		System.out
				.println("------------------------------------------------------------------------------");
		double total_preis = 0;
		for (Gut gut : this.tdg.gut_liste) {

			total_preis += round(gut.getPreis());

		}
		System.out.println("M_out_summe ist          " + m_out_summe);
		System.out.println("Gesamt gueterpreis ist:  " + round(total_preis));

	}

	public void berechneGesamteAnfragen(double temp_letze_schicht) {
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			if (kaeufer.getSchicht() == temp_letze_schicht - 1) {
				kaeufer.setSumAnfragen(0);
			}
		}

		for (Kante kante : this.geschichteNetzwerk) {
			if ((kante.getGutKnoten().getSchicht() == temp_letze_schicht)) {
				for (Kante kante1 : this.geschichteNetzwerk) {
					if (kante1.getGutKnoten().getName()
							.equals(kante.getGutKnoten().getName())
							&& (kante1.getKaeuferKnoten().getSchicht() == kante
									.getGutKnoten().getSchicht() - 1)) {
						kante.getKaeuferKnoten()
								.setSumAnfragen(
										kante.getKaeuferKnoten()
												.getSumAnfragen()
												+ (((round(kante.getGutKnoten()
														.getPreis()) / round(berechnePreissummeSelektierteGueter())) + round(berechne_red_j(kante
														.getGutKnoten()))
														/ kante.getGutKnoten()
																.getAnzahlKanten())));
					}
				}
			}
		}
	}

	private double berechne_red_j(Gut end_knoten) {
		if (end_knoten.getSchicht() == this.getLetzteSchicht()) {
			return 0;
		} else {
			double temp_red_j = 0.0;
			for (Kante kante : this.geschichteNetzwerk) {
				Kaeufer kaeufer = kante.getKaeuferKnoten();
				if (kaeufer.getSchicht() == end_knoten.getSchicht() + 1) {
					temp_red_j = temp_red_j
							+ kaeufer.getmProGut().get(end_knoten);
				}
			}
			return round(temp_red_j);
		}
	}

	private double berechnePreissummeSelektierteGueter() {
		double temp_summe = 0.0;
		for (Gut gut : this.tdg.gut_liste) {
			if (gut.isSelektiert()) {
				temp_summe += gut.getPreis();
			}
		}
		this.setGesamtGutPreis(round(temp_summe));
		return round(this.gesamtGutPreis);
	}

	private boolean compareKante(Kante kante) {
		boolean res = false;
		for (Kante listKante : this.geschichteNetzwerk) {
			if (listKante.getGutKnoten().getName()
					.equals(kante.getGutKnoten().getName())
					&& listKante.getKaeuferKnoten().getName()
							.equals(kante.getKaeuferKnoten().getName())) {
				res = true;
			}

		}
		return res;
	}

	public void bilde_geschichtete_netzwerk(Kaeufer kaeufer) {
		this.geschichteNetzwerk.clear();
		resetSelektion();
		kaeufer.setSelektiert(true);
		kaeufer.setSchicht(1);
		for (Gut gut : this.tdg.gut_liste) {
			if (Math.abs(kaeufer.getRelativNutzen().get(gut)
					- kaeufer.getAlpha()) <= 0.001) {
				Kante kante = new Kante(kaeufer, gut);
				if (!this.compareKante(kante)) {
					this.geschichteNetzwerk.add(kante);
					gut.setSchicht(kaeufer.getSchicht() + 1);
					gut.setSelektiert(true);
					gut.setAnzahlKanten(gut.getAnzahlKanten() + 1);
					this.letzteSchicht = kaeufer.getSchicht() + 1;
				}
			}
		}

		bezahleVerbundeneGueter(kaeufer);
		if (!kaeufer.isSaturiert()) {
			if (checkKaeuferSchicht(2)) {
				baueKaeuferSchicht(2);
			}
		}
	}

	private void baueKaeuferSchicht(int schicht_marker) {
		for (Gut gut : this.tdg.gut_liste) {
			if (gut.getSchicht() == schicht_marker) {
				for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
					if (kaeufer.isSaturiert()
							&& (!kaeufer.isSelektiert() || kaeufer.getSchicht() == schicht_marker + 1)
							&& kaeufer.getRelativNutzen().get(gut) == kaeufer
									.getAlpha()) {
						Kante new_kante = new Kante(kaeufer, gut);
						if (!compareKante(new_kante)) {
							this.geschichteNetzwerk.add(new_kante);
							kaeufer.setSelektiert(true);
							kaeufer.setSchicht(schicht_marker + 1);
						}
					}
				}
			}
		}
		if (checkGueterSchicht(schicht_marker + 1)) {
			baueGueterSchicht(schicht_marker + 1);
		}
	}

	private void baueGueterSchicht(int schicht_marker) {
		for (Kante kante : this.geschichteNetzwerk) {
			Kaeufer kaeufer = kante.getKaeuferKnoten();
			if (kaeufer.getSchicht() == schicht_marker) {
				for (Gut gut : this.tdg.gut_liste) {
					if (Math.abs(kaeufer.getRelativNutzen().get(gut)
							- kaeufer.getAlpha()) <= 0.001
							&& ((gut.getSchicht() == schicht_marker + 1) || (!gut
									.isSelektiert()))) {
						// System.out.println(" True");
						Kante neue_kante = new Kante(kaeufer, gut);
						if (!compareKante(kante)) {
							neue_kante.getGutKnoten().setSelektiert(true);
							neue_kante.getGutKnoten()
									.setAnzahlKanten(
											neue_kante.getGutKnoten()
													.getAnzahlKanten() + 1);
							neue_kante.getGutKnoten().setSchicht(
									schicht_marker + 1);
							this.setLetzteSchicht(neue_kante.getGutKnoten()
									.getSchicht());
							this.geschichteNetzwerk.add(neue_kante);
						}
					}

				}
			}
		}

		if (checkKaeuferSchicht(schicht_marker + 1)) {
			baueKaeuferSchicht(schicht_marker + 1);
		}
	}

	private boolean checkGueterSchicht(int schicht_marker) {
		boolean result = false;
		for (Kante kante : this.geschichteNetzwerk) {
			Kaeufer kaeufer = kante.getKaeuferKnoten();
			if (kaeufer.getSchicht() == schicht_marker) {
				for (Gut gut : this.tdg.gut_liste) {
					if (Math.abs(kaeufer.getRelativNutzen().get(gut)
							- kaeufer.getAlpha()) <= 0.001
							&& ((gut.getSchicht() == schicht_marker + 1) || (!gut
									.isSelektiert()))) {
						result = true;
					} else {
						result = false;
					}
				}
			}
		}
		return result;
	}

	private boolean checkKaeuferSchicht(int schicht_marker) {
		boolean result = false;
		for (Kante kante : this.geschichteNetzwerk) {
			Gut gut = kante.getGutKnoten();
			if (gut.getSchicht() == schicht_marker) {
				for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
					if (kaeufer.isSaturiert()
							&& !kaeufer.isSelektiert()
							&& Math.abs(kaeufer.getRelativNutzen().get(gut)
									- kaeufer.getAlpha()) <= 0.001) {
						result = true;
					}
				}
			} else {
				result = false;
			}
		}
		return result;
	}

	private void bezahleVerbundeneGueter(Kaeufer kaeufer) {
		for (Kante kante : this.geschichteNetzwerk) {
			Gut gut = kante.getGutKnoten();
			if (kante.getKaeuferKnoten().getName().equals(kaeufer.getName())
					&& gut.getSchicht() == kaeufer.getSchicht() + 1) {
				if (kaeufer.getRestKapital() >= gut.getRestPreis()) {
					kaeufer.setRestKapital(kaeufer.getRestKapital()
							- gut.getRestPreis());
					kaeufer.getmProGut().put(gut, gut.getRestPreis());
					kaeufer.setmOut(kaeufer.getmOut() + gut.getRestPreis());
					gut.setRestPreis(0);
					gut.setAusverkauft(true);
				} else {
					gut.setRestPreis(gut.getRestPreis()
							- kaeufer.getRestKapital());
					kaeufer.getmProGut().put(gut, kaeufer.getRestKapital());
					kaeufer.setmOut(kaeufer.getmOut()
							+ kaeufer.getRestKapital());
					kaeufer.setRestKapital(0);
					kaeufer.setSaturiert(true);
				}
			}
		}
	}

	private void resetSelektion() {
		for (Gut gut : this.tdg.gut_liste) {
			gut.setSelektiert(false);
			gut.setSchicht(1);
			gut.setAnzahlKanten(0);
		}
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			kaeufer.setSelektiert(false);
			kaeufer.setM1(0);
			kaeufer.setM2(0);
		}
	}

	public void berechneRelativeNutzenFuerKaeufer() {
		for (Kaeufer k : this.tdg.kaeufer_liste) {
			Iterator<Gut> it = k.getNutzen().keySet().iterator();

			while (it.hasNext()) {
				Gut gut = it.next();
				double relativNutzen = round(k.getNutzen().get(gut)
						/ gut.getPreis());
				k.getRelativNutzen().put(gut, relativNutzen);
			}
			k.berechneAlpha();
			k.berechneBeta();
		}
	}

	public void bauIdentifikationsgraph() {
		this.identi_graph.getKanten().clear();
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			Iterator<Gut> it = kaeufer.getRelativNutzen().keySet().iterator();
			while (it.hasNext()) {
				Gut gut = it.next();
				if (Math.abs(kaeufer.getRelativNutzen().get(gut)
						- kaeufer.getAlpha()) <= 0.001) {
					gut.setVerbunden(true);
					this.identi_graph.addKante(new Kante(kaeufer, gut));
				}
			}
		}
		verbindeLoseGueter();
	}

	public void verbindeLoseGueter() {
		for (int i = 0; i < this.tdg.gut_liste.size(); i++) {
			Gut gut = this.tdg.gut_liste.get(i);
			if (!gut.isVerbunden()) {
				updateGutPreis(gut);
				verbindeGutKaeufer(gut);
			}

		}
	}

	private void verbindeGutKaeufer(Gut gut) {
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			if (Math.abs(kaeufer.getNutzen().get(gut) - kaeufer.getAlpha()) <= 0.001) {
				gut.setVerbunden(true);
				this.identi_graph.addKante(new Kante(kaeufer, gut));
			}
		}
	}

	private void updateGutPreis(Gut gut) {
		double neuer_preis = 0.0;
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			double temp_preis = round(kaeufer.getNutzen().get(gut)
					/ kaeufer.getAlpha());
			if (neuer_preis <= temp_preis) {
				neuer_preis = temp_preis;
			}
		}

		gut.setPreis(neuer_preis);
		gut.setRestPreis(neuer_preis);
		updateRelativeNutzen(gut);
	}

	private void updateRelativeNutzen(Gut gut) {
		for (Kaeufer kaeufer : this.tdg.kaeufer_liste) {
			kaeufer.getRelativNutzen().put(gut,
					round(kaeufer.getNutzen().get(gut) / gut.getPreis()));
			kaeufer.berechneAlpha();
			kaeufer.berechneBeta();
		}
	}

	public double round(double zahl) {
		return Math.round(zahl * 100) / 100.0d;
	}

	public TestDatenGenerator getTDG() {
		return this.tdg;
	}

	public void printKaeuferList() {
		for (Kaeufer k : tdg.kaeufer_liste) {
			k.printKaeufer();
		}
	}

	public double getPreisErhoehung() {
		return preisErhoehung;
	}

	public void setPreisErhoehung(double preisErhoehung) {
		this.preisErhoehung = preisErhoehung;
	}
}
